package com.mpgame.connection;

import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import com.mpgame.common.mq.KafkaBus;
import com.mpgame.common.proto.CmdId;
import com.mpgame.common.proto.ErrorCode;
import com.mpgame.common.proto.GamePacket;
import com.mpgame.common.proto.MQPlayerOfflineMsg;
import com.mpgame.common.proto.PlayerLeaveMsg;
import com.mpgame.common.utils.Log;
import com.mpgame.database.redis.model.UserTokenInfo;
import com.mpgame.database.redis.utils.PlayerOnlineUtil;
import com.mpgame.database.redis.utils.TokenUtil;

import io.netty.channel.Channel;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.channel.group.ChannelGroup;

public final class ChannelRegistry {
    private static final ChannelGroup GROUP = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    private static final Map<String, Channel> PLAYER_CONNECTIONS = new ConcurrentHashMap<>();
    private static final Map<Channel, String> CONNECTION_PLAYERS = new ConcurrentHashMap<>();
    private static final Set<Channel> AUTHENTICATED_CONNECTIONS = Collections.newSetFromMap(new ConcurrentHashMap<>());

    private ChannelRegistry() {
    }

    public static void add(Channel ch) {
        GROUP.add(ch);
    }

    public static void remove(Channel ch) {
        GROUP.remove(ch);

        String playerId = CONNECTION_PLAYERS.remove(ch);
        if (playerId != null) {
            try {
                // 检查是否还有其他活跃连接
                Channel currentConnection = PLAYER_CONNECTIONS.get(playerId);
                if (currentConnection == ch) {
                    // 当前连接就是要移除的连接
                    PLAYER_CONNECTIONS.remove(playerId);
                    AUTHENTICATED_CONNECTIONS.remove(ch);

                    // 检查是否还有其他连接
                    if (!PLAYER_CONNECTIONS.containsKey(playerId)) {
                        PlayerOnlineUtil.removePlayerFromOnlineList(playerId);
                        // 将玩家离线消息放入消息队列（用于服务端使用）
                        int playerIdInt = Integer.parseInt(playerId);
                        // 向消息队列中置入玩家离线消息
                        KafkaBus.sendServerMsg(
                            playerIdInt, 
                            CmdId.GAME_PLAYER_LEAVE, 
                            MQPlayerOfflineMsg.newBuilder()
                            .setAccountId(playerIdInt)
                            .setOfflineTime(System.currentTimeMillis())
                            .setReason("disconnect")
                            .build());
                        // 向客户端广播玩家离线消息
                        GamePacket leavePkt = GamePacket.newBuilder()
                                .setCmd(CmdId.GAME_PLAYER_LEAVE)
                                .setEc(ErrorCode.SUCCESS)
                                .setData(PlayerLeaveMsg.newBuilder().setAccountId(Integer.parseInt(playerId)).build()
                                        .toByteString())
                                .build();
                        ChannelRegistry.broadcast(leavePkt, ch);
                        Log.info("玩家 " + playerId + " 所有连接已移除，从在线列表移除");
                    } else {
                        Log.info("玩家 " + playerId + " 仍有其他连接，保持在线状态");
                    }
                } else {
                    // 有其他连接，只移除当前连接
                    AUTHENTICATED_CONNECTIONS.remove(ch);
                    Log.info("玩家 " + playerId + " 旧连接已移除，但仍有其他连接");
                }
            } catch (Exception e) {
                Log.info("玩家 " + playerId + " 连接移除失败");
            }
        }
    }

    public static void kick(Channel ch, String reason) {
        if (ch == null) {
            Log.warn("踢出玩家连接，但其连接不存在");
            return;
        }

        if (ch.isActive()) {
            Log.info("服务器踢出玩家，原因：" + reason);
            ch.close(); // 触发 pipeline 的 channelInactive 事件
        } else {
            // 连接已失效，但 map 里还没清理，则手动清理
            remove(ch);
        }
    }

    /** 广播消息，广播给所有连接 */
    public static void broadcast(Object msg) {
        GROUP.writeAndFlush(msg, channel -> AUTHENTICATED_CONNECTIONS.contains(channel));
    }

    /** 广播消息，排除指定连接 */
    public static void broadcast(Object msg, Channel excludeChannel) {
        GROUP.writeAndFlush(msg, channel -> {
            return AUTHENTICATED_CONNECTIONS.contains(channel) && !channel.equals(excludeChannel);
        });
    }

    /** 广播消息，排除多个玩家ID的连接 */
    public static void broadcast(Object msg, Set<String> excludeAccountIds) {
        GROUP.writeAndFlush(msg, channel -> {
            // 只向已认证连接广播，且排除指定玩家列表
            if (!AUTHENTICATED_CONNECTIONS.contains(channel)) {
                return false;
            }

            String playerId = CONNECTION_PLAYERS.get(channel);
            return playerId == null || !excludeAccountIds.contains(playerId);
        });
    }

    /** 广播消息，排除指定玩家ID的连接 */
    public static void broadcast(Object msg, String excludeAccountId) {
        GROUP.writeAndFlush(msg, channel -> {
            // 只向已认证连接广播，且排除指定玩家
            if (!AUTHENTICATED_CONNECTIONS.contains(channel)) {
                return false;
            }

            String playerId = CONNECTION_PLAYERS.get(channel);
            return playerId == null || !playerId.equals(excludeAccountId);
        });
    }

    /** 多播消息 */
    public static void multicast(Object msg, Set<String> includeAccountIds) {
        if (msg == null || includeAccountIds == null || includeAccountIds.isEmpty())
            return;
        for (String id : includeAccountIds) {
            Channel ch = getPlayerConnection(id);
            if (ch != null && AUTHENTICATED_CONNECTIONS.contains(ch)) {
                ch.writeAndFlush(msg);
            }
        }
    }

    /** 向指定玩家发送游戏包 */
    public static boolean sendTo(String playerId, Object pkt) {
        Channel channel = PLAYER_CONNECTIONS.get(playerId);
        if (channel == null || pkt == null) {
            Log.warn("尝试向 " + playerId + " 发送消息，但其连接为null");
            return false;
        }
        channel.writeAndFlush(pkt);
        return true;
    }

    /** 向指定玩家发送游戏包 */
    public static boolean sendTo(Integer playerId, Object pkt) {
        return sendTo(String.valueOf(playerId), pkt);
    }

    /** 向指定玩家发送消息体 */
    public static boolean sendTo(String playerId, CmdId msgType, Message msg) {
        return sendTo(playerId, GamePacket.newBuilder()
                .setCmd(msgType)
                .setData(msg.toByteString())
                .build());
    }

    /** 向指定玩家发送消息体 */
    public static boolean sendTo(Integer playerId, CmdId msgType, ByteString bytes) {
        return sendTo(String.valueOf(playerId), msgType, bytes);
    }

    /** 向指定玩家发送消息体 */
    public static boolean sendTo(String playerId, CmdId msgType, ByteString bytes) {
        return sendTo(playerId, GamePacket.newBuilder()
                .setCmd(msgType)
                .setData(bytes)
                .build());
    }

    /** 向指定玩家发送消息体 */
    public static boolean sendTo(int playerId, CmdId msgType, Message msg) {
        return sendTo(String.valueOf(playerId), msgType, msg);
    }

    public static void addPlayerConnection(String playerId, Channel ch) {
        Channel oldChannel = PLAYER_CONNECTIONS.get(playerId);
        if (oldChannel != null && oldChannel.isActive()) {
            Log.warn("玩家 " + playerId + "先前已有连接，已断开旧连接");
            oldChannel.close();
        }
        PLAYER_CONNECTIONS.put(playerId, ch);
        CONNECTION_PLAYERS.put(ch, playerId);
        AUTHENTICATED_CONNECTIONS.add(ch);
        Log.info(String.valueOf(PlayerOnlineUtil.getAllOnlinePlayerInfos()));
        Log.info("玩家 " + playerId + " 连接已完成验证，当前玩家数：" + PLAYER_CONNECTIONS.size());
    }

    public static Channel getPlayerConnection(String playerId) {
        Channel res = PLAYER_CONNECTIONS.get(playerId);
        if (res == null)
            Log.warn("尝试获取 " + playerId + " 的连接，但其连接为null");
        return res;
    }

    public static Channel getPlayerConnection(int playerId) {
        return getPlayerConnection(String.valueOf(playerId));
    }

    public static Set<Channel> getPlayerConnections() {
        return AUTHENTICATED_CONNECTIONS;
    }

    public static String getAccountId(Channel ch) {
        String res = CONNECTION_PLAYERS.get(ch);
        if (res == null)
            Log.warn("尝试获取 " + ch + " 的玩家ID，但其为null");
        return res;
    }

    public static UserTokenInfo getUseTokenInfoFromChannel(Channel ch) {
        String playerId = CONNECTION_PLAYERS.get(ch);
        if (playerId == null) {
            Log.warn("尝试获取 " + ch + " 的token信息，但连接不存在或未验证");
            return null;
        }
        return TokenUtil.getUserTokenInfoFromAccountId(Integer.parseInt(playerId));
    }

    public static boolean getAuthenticated(Channel ch) {
        return AUTHENTICATED_CONNECTIONS.contains(ch);
    }

    public static int size() {
        return GROUP.size();
    }
}
